初始方法一:
疑虑:model使用upload_to自定义路径方法失效,指定路径也失效。最后以Views中指定MEDIA_URL和MEDIA_ROOT做拼接,并且自行判断并建立文件夹,手动chunk保存。不完美的解决。
model: 看avatar字段的upload_to
import uuidfrom django.db import modelsfrom django.contrib.auth.models import AbstractUserclass SysUser(AbstractUser): """ 用户扩展表,用于替换auth的user类,并添加头像和昵称。 """ uid = models.BigAutoField(primary_key=True) nickname = models.CharField(max_length=32, verbose_name="昵称") # 使用 models.ImageField 需要安装 Pillow ,cmd命令:pip install Pillow avatar = models.ImageField(verbose_name="头像", upload_to="avatar11") class Meta: # 数据库显示的表名 db_table = "sys_user"
form:
from django.forms import Formfrom django.forms import widgetsfrom django.forms import fieldsclass EditUserForm(Form): """...""" e_avatar = fields.ImageField( required=False, widget=widgets.FileInput(attrs={"hidden": "hidden"}) ) """..."""
Views:
def upload_user_avatar(request): """用户上传头像操作""" if request.method == "POST": user_id = request.user.uid # 只是当前登录用户 ret = {'state': True, 'error': None, 'data': None} # user_id = request.POST.get("uid", None) avatar = request.FILES.get("avatar", None) # 如果文件夹不存在,则建立 dir_path = os.path.join(MEDIA_ROOT, "users", "{}_{}".format(request.user.uid, request.user.username), "avatar") if not os.path.exists(os.path.join(dir_path)): os.makedirs(os.path.join(dir_path)) avatar_path = os.path.join(dir_path, avatar.name) try: with open(avatar_path, 'wb+') as img: for chunk in avatar.chunks(): img.write(chunk) print("文件接收成功") img_url = os.path.join("users", "{}_{}".format(request.user.uid, request.user.username), "avatar") SysUser.objects.filter(uid=user_id).update(avatar=img_url) ret["data"] = "头像更新成功" except Exception as e: ret["state"] = False ret["data"] = "头像更新失败,请确认图片格式" print(e) return HttpResponse(json.dumps(ret))
html:
- SysUser.objects.filter(uid=user_id).update(avatar=img_url)
- 这句话执行结束后,只保存文件名,无法使用upload_to指定的路径。
- 页面加载时,直接使用拼接后路径。
初始方法二:
TMD,一个保存方式的问题,竟然能啰啰嗦嗦这么久。以后以此方法操作。
model:不变
views:
def upload_user_avatar(request): """用户上传头像操作""" if request.method == "POST": user_id = request.user.uid # 只是当前登录用户 ret = {'state': True, 'error': None, 'data': None} avatar = request.FILES.get("avatar", None) try: user_obj = SysUser.objects.filter(uid=user_id).first() user_obj.avatar = avatar user_obj.save() ret["data"] = "头像更新成功" except Exception as e: ret["state"] = False ret["data"] = "头像更新失败,请确认图片格式" print(e) return HttpResponse(json.dumps(ret))
- user_obj.save()
- 使用这种保存方式的好处:
- 会自动保存文件路径到数据库
- 会自动保存文件到指定路径
- 必要时会自动创建目录。
目前最终方法:
model:
import uuidimport osfrom django.db import modelsfrom django.contrib.auth.models import AbstractUser"""python-cmd : makemigrationspython-cmd : migrate"""def user_directory_path(instance, filename): """用户上传文件时的自定义操作""" ext = filename.split('.')[-1] # 获取上传的文件名的后缀 filename = '{0}.{1}'.format(uuid.uuid4().hex[:8], ext) # 修改文件名,谨防重名。(文档说重名后,会自动增加后缀) sub_folder = 'avatar' # 日后如果对上传文件的类型进行归档时可以COPY这个做参考。 # if ext.lower() in ["jpg", "png", "gif", "ico"]: # sub_folder = "avatar" # if ext.lower() in ["pdf", "docx"]: # sub_folder = "document" return os.path.join("users", "%s-%s" % (instance.user.uid, instance.user.username), sub_folder, filename)class SysUser(AbstractUser): """ 用户扩展表,用于替换auth的user类,并添加头像和昵称。 """ uid = models.BigAutoField(primary_key=True) nickname = models.CharField(max_length=32, verbose_name="昵称") # 使用 models.ImageField 需要安装 Pillow ,cmd命令:pip install Pillow avatar = models.ImageField(verbose_name="头像", upload_to=user_directory_path) class Meta: # 数据库显示的表名 db_table = "sys_user"
form:
from django.forms import Formfrom django.forms import widgetsfrom django.forms import fieldsclass EditUserForm(Form): """...""" e_avatar = fields.ImageField( required=False, widget=widgets.FileInput(attrs={"hidden": "hidden"}) ) """页面操作该元素时,使用的ajax方式,所以仅对此项进行操作。未使用EditUserForm做验证等操作。""" """..."""
views:
def upload_user_avatar(request): """用户上传头像操作""" if request.method == "POST": user_id = request.user.uid # 只是当前登录用户 ret = {'state': True, 'error': None, 'data': None} # user_id = request.POST.get("uid", None) # 如果以后有需求修改其他人的信息时,再从页面获取。 avatar = request.FILES.get("avatar", None) try: user_obj = SysUser.objects.filter(uid=user_id).first() user_obj.avatar = avatar user_obj.save() ret["data"] = "头像更新成功" except Exception as e: ret["state"] = False ret["data"] = "头像更新失败,请确认图片格式" print(e) return HttpResponse(json.dumps(ret))
html:
JS:
// 上传文件按钮(label里的图片)点击事件$('#id_e_avatar').on('change',function () { // 文件上传之前在本地加载预览。 { // 获取用户最后一次选择的图片 var choose_file=$(this)[0].files[0]; // 创建一个新的FileReader对象,用来读取文件信息 var reader=new FileReader(); // 读取用户上传的图片的路径 reader.readAsDataURL(choose_file); // 读取完毕之后,将图片的src属性修改成用户上传的图片的本地路径 reader.onload=function () { $("#avatar-img").attr("src",reader.result) } } // 使用FormData格式,将文件上传至服务器 formdata = new FormData(); //formdata.append('uid',$('#id_e_id').val()); formdata.append("avatar",$("#id_e_avatar")[0].files[0]); $.ajax({ type: "POST", url: "/account/upload_user_avatar", data:formdata, processData:false, //上传文件时需要如此设置1 contentType:false, //上传文件时需要如此设置2 success:function (data) { data = JSON.parse(data); console.log(data); alert(data.data) // 偷懒了,哈哈 } });});
media_url映射:
from django.urls import path, re_path, includefrom django.views.generic.base import RedirectViewfrom django.views.static import serve # 映射media_root路径,可以通过浏览器访问的操作-1from django.contrib.auth.decorators import login_requiredfrom WXH_DJANGOWEB import settingsfrom utils.required import requiredurlpatterns = [ path('account/', include("account.urls")), path('blog/', include("blog.urls")), re_path(r"^media/(?P.*)$", serve, {"document_root": settings.MEDIA_ROOT, }) # 映射media_url,可以通过浏览器访问的操作-2]
至此,记录完毕。回家吃饭。坑爹的保存方式…… 愿同志们绕坑而行,用不遇坑。